﻿package com.calefk.modules.perspective {
	
    import com.calefk.modules.math.Vector2D;
    import com.calefk.modules.math.Vector3D;
	import com.calefk.util.Generic;
    
    public class Camera3D {
        
        // son vectores unitarios
        private var mUp:Vector3D; // y
        private var mRight:Vector3D; // x
        private var mLook:Vector3D; // z
        
        private var mViewPoint:Vector3D; // x, y, z
        private var mViewDirection:Vector3D; // z
        
        // position relative to world
        private var mCameraPoint:Vector3D;
        
        public function get v_up():Vector3D { return mUp; }
        public function get v_vright():Vector3D { return mRight; }
        public function get v_look():Vector3D { return mLook; }
        
        public function get viewPoint():Vector3D { return mViewPoint; }
        public function get cameraPoint():Vector3D { return mCameraPoint; }
        
        private var mVpx:Number = 600 / 2;
        private var mVpy:Number = 400 / 2;
        
        private var mFL:Number = 300;
        
        public function get focalLength():Number { return mFL; };
        
        public function setVanishingPoint(vpx:Number , vpy:Number):void {
            mVpx = vpx;
            mVpy = vpy;
        }
        
        public function setViewPosition(px:Number , py:Number , pz:Number):void {
            mViewPoint.x = px;
            mViewPoint.y = py;
            mViewPoint.z = pz;
        }
        
        public function Camera3D() {
            mViewPoint = new Vector3D();
            mCameraPoint = new Vector3D();
            
            // init position
            setViewPosition(5000 , 50 , 5000);
            
            mUp = new Vector3D(0, 1, 0);
            mUp.normalize();
            
            mRight = new Vector3D(1,0,0);
            mRight.normalize();
            
            mViewDirection = new Vector3D(0, 0 , 1);
            mViewDirection.normalize();
            
            
            // calculando la posicion inicial de la camara
            var veyeplane:Vector3D = Vector3D.mult(mViewDirection , mFL);
            mCameraPoint = Vector3D.sum(mViewPoint , veyeplane);
            
            // yaw(Generic.degreesToRad(-45)); 
            // yaw(Generic.degreesToRad(10));   // angulo positivo es antihorario  (visto desde el eje Y)
            //  pitch(Generic.degreesToRad(5)); // angulo positivo es horario      (visto desde el eje X)
            // roll(Generic.degreesToRad(60));  // angulo positivo es antihorario  (visto desde el eje Z)
        }
        
        public function pitch(angle:Number):void {
            Vector3D.rotateX(mUp , angle);
            Vector3D.rotateX(mViewDirection , angle);
            
            mRight = Vector3D.cross(mUp , mViewDirection);
            mRight.normalize();
            
            // actualizo el punto de la camara
            var veyeplane:Vector3D = Vector3D.mult(mViewDirection , mFL);
            mCameraPoint = Vector3D.sum(mViewPoint , veyeplane);
            
        }
        
        public function yaw(angle:Number):void {
            Vector3D.rotateY(mUp , angle);
            Vector3D.rotateY(mViewDirection  , angle);
            
            mRight = Vector3D.cross(mUp , mViewDirection);
            mRight.normalize();
        }
        
        public function roll(angle:Number):void {
            Vector3D.rotateZ(mUp , angle);
            Vector3D.rotateZ(mViewDirection  , angle);          
            
            mRight = Vector3D.cross(mUp , mViewDirection);
            mRight.normalize();
        }
        
        public function update(dt:int):void {
            
        }
        
        public function render(entity:Entity3D):void {
            // convierto la posicion del mundo a la posiciond e la camara
            var screen:Vector3D = getScreenPoint(entity.wposition);
            if (screen) {
                entity.x = screen.x;
                entity.y = screen.y;
                entity.scale(screen.z);
                entity.visible(true);
            } else {
                entity.visible(false);
            }
        }
        /**
         * 
         * @param	positionWorld
         * @return devuelve un vector3D x , y => son posiciones de pantalla , z es scala
         */
        public function getScreenPoint(pworld:Vector3D):Vector2D {
            var eCamera:Vector3D = worldToCamera(pworld);
            var point2D:Vector3D = null;
            
            // si se dibuja en pantalla
            if (eCamera.z > -mFL) {
                var point2D:Vector3D = new Vector3D();
                var scale:Number = mFL / (mFL + eCamera.z);
                point2D.z = scale;
                point2D.x = mVpx +  eCamera.x * scale;
                point2D.y = mVpy + -eCamera.y * scale;
            }
            
            return point2D;
        }
        
        public function worldToCamera(pworld:Vector3D):Vector3D {
             // actualizo la position de la camara
            var veyeplane:Vector3D = Vector3D.mult(mViewDirection , mFL);
            mCameraPoint = Vector3D.sum(mViewPoint , veyeplane);
            
            // se btiene el vector de la camara hacia el punto de la entidad
            var ventity:Vector3D = Vector3D.sub(pworld , mCameraPoint);

            // se obtiene las coordenas con respecto a la camara
            var cy:Number = Vector3D.dotProduct(ventity , mUp);
            var cx:Number = Vector3D.dotProduct(ventity , mRight);
            var cz:Number = Vector3D.dotProduct(ventity , mViewDirection);
            
            var pCamera:Vector3D = new Vector3D(cx , cy , cz);
            return pCamera;
        }
    }
}